package com.snake.win.web;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Net;
import com.badlogic.gdx.net.ServerSocket;
import com.badlogic.gdx.net.ServerSocketHints;
import com.badlogic.gdx.net.Socket;
import com.badlogic.gdx.net.SocketHints;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class Server {
    ServerSocket serverSocket;
    int port = 12345;
    private final List<Socket> connectedClients = new CopyOnWriteArrayList<>(); // Thread-safe list for clients
    private Thread acceptingThread;
    private boolean running = false;

    public void start() {
        if (running) {
            Gdx.app.log("Server", "Server is already running.");
            return;
        }
        try {
            ServerSocketHints serverSocketHints = new ServerSocketHints();
            // Set any specific hints if needed, e.g., performancePreferences
            serverSocket = Gdx.net.newServerSocket(Net.Protocol.TCP, port, serverSocketHints);
            running = true;
            Gdx.app.log("Server", "Server started on port " + port);

            acceptingThread = new Thread(() -> {
                while (running) {
                    try {
                        Socket clientSocket = serverSocket.accept(null); // Accept new connection
                        Gdx.app.log("Server", "Client connected: " + clientSocket.getRemoteAddress());
                        connectedClients.add(clientSocket);
                        // Optional: Start a new thread here to handle this specific client's messages
                        // For now, we just add them to the list.
                        new Thread(new ClientHandler(clientSocket)).start();
                    } catch (Exception e) {
                        if (running) { // Only log error if server is supposed to be running
                            Gdx.app.error("Server", "Error accepting client connection", e);
                        }
                    }
                }
            });
            acceptingThread.start();

        } catch (Exception e) {
            Gdx.app.error("Server", "Error starting server", e);
            running = false;
        }
    }

    public void stop() {
        running = false;
        if (acceptingThread != null) {
            acceptingThread.interrupt(); // Interrupt the accepting thread
        }
        for (Socket client : connectedClients) {
            try {
                client.dispose(); // Close client sockets
            } catch (Exception e) {
                Gdx.app.error("Server", "Error closing client socket", e);
            }
        }
        connectedClients.clear();
        if (serverSocket != null) {
            try {
                serverSocket.dispose(); // Close the server socket
                Gdx.app.log("Server", "Server stopped.");
            } catch (Exception e) {
                Gdx.app.error("Server", "Error stopping server", e);
            }
        }
    }

    public void broadcastMessage(String message) {
        List<Socket> clientsToRemove = new ArrayList<>();
        for (Socket client : connectedClients) {
            try {
                PrintWriter writer = new PrintWriter(client.getOutputStream(), true);
                writer.println(message);
                // Gdx.app.log("Server", "Broadcasted to " + client.getRemoteAddress() + ": " + message);
            } catch (Exception e) {
                Gdx.app.error("Server", "Error broadcasting to client " + client.getRemoteAddress(), e);
                clientsToRemove.add(client); // Mark for removal if error (e.g., disconnected)
            }
        }
        // Remove clients that caused an error (likely disconnected)
        for (Socket clientToRemove : clientsToRemove) {
            removeClient(clientToRemove);
        }
    }

    private void removeClient(Socket clientSocket) {
        if (clientSocket == null) return;
        connectedClients.remove(clientSocket);
        try {
            Gdx.app.log("Server", "Client disconnected: " + clientSocket.getRemoteAddress());
            clientSocket.dispose();
        } catch (Exception e) {
            Gdx.app.error("Server", "Error disposing disconnected client socket", e);
        }
    }

    // Placeholder for handling messages from a specific client
    // This would typically run in a separate thread per client.
    private class ClientHandler implements Runnable {
        private Socket clientSocket;
        private BufferedReader reader;

        public ClientHandler(Socket socket) {
            this.clientSocket = socket;
            try {
                this.reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            } catch (IOException e) {
                Gdx.app.error("Server", "Error creating reader for client " + clientSocket.getRemoteAddress(), e);
                removeClient(clientSocket);
            }
        }

        @Override
        public void run() {
            try {
                String line;
                while (running && clientSocket.isConnected() && (line = reader.readLine()) != null) {
                    Gdx.app.log("Server", "Received from " + clientSocket.getRemoteAddress() + ": " + line);
                    // Process message 'line'
                    // For example, parse it and update game state, then broadcast.
                    // For now, just echo back or broadcast.
                    broadcastMessage("Client " + clientSocket.getRemoteAddress() + " said: " + line);
                }
            } catch (IOException e) {
                if (running) { // Don't log error if server is stopping and caused this
                    Gdx.app.log("Server", "Client " + clientSocket.getRemoteAddress() + " connection error or closed.", e);
                }
            } finally {
                removeClient(clientSocket);
            }
        }
    }
     public List<Socket> getConnectedClients() {
        return connectedClients;
    }
}
